-- -- Squawk Hard SquawkHard = LibStub( "AceAddon-3.0" ):NewAddon( "SquawkHard", "AceConsole-3.0", "AceEvent-3.0", "AceTimer-3.0" ); local media = LibStub:GetLibrary("LibSharedMedia-3.0"); local shFrame, cooldownFrame, target, spellTexture, overlayTexture; -- spell info local wrathId = 48461; local starfireId = 48465; local moonfireId = 48463; local insectSwarmId = 48468; local faerieFireId = 770; local wrathName = GetSpellInfo(wrathId); local starfireName = GetSpellInfo(starfireId); local moonfireName = GetSpellInfo(moonfireId); local insectSwarmName = GetSpellInfo(insectSwarmId); local faerieFireName = GetSpellInfo(faerieFireId); local bloodlustName; local solarEclipseId = 48517; -- starfire procced eclipse, improves wrath damage local lunarEclipseId = 48518; -- wrath procced eclipse, improves starfire crit local cooldownSpellName = GetSpellInfo(768); -- use cat form, a physical ability so we don't get spell lockout values local moonfireExtends = 0; local eclipseDuration = 15; local eclipseCooldown = 30; local insectSwarmTrained = true; local activeMode = false; local lastSpell; local groupCount = 0; local hasSFGlyph = false; -- configs local defaults = { char = { x = 0, y = 0, relativePoint = "CENTER", size = 50, locked = false, lowHealthEclipseProcType = 2, highHealthEclipseProcType = 1, twist = false, -- alternate between Wrath and Starfire to proc eclipse useInsectSwarm = true, useFaerieFire = false, lunarEclipseMoonfireThreshold = 8, -- atleast this many seconds left on lunar eclipse left to consider casting moonfire lunarEclipseInsectSwarmThreshold = 16, -- atleast this many seconds of lunar eclipse left to consider casting insect swarm solarEclipseMoonfireThreshold = 10, -- atleast this many seconds of solar eclipse left to consider casting moonfire solarEclipseInsectSwarmThreshold = 8, -- atleast this many seconds of solar eclipse left to consider casting insect swarm baseDotThreshold = 11000, -- only apply dots if the units hp is greater than this dotThresholdPartyModifier = 1000, -- add this amount to the baseDotThreshold per party member baseLowHealthThreshold = 2200, -- if the unit has less than this amount of hp just cast starfire lowHealthPartyModifier = 500, -- add this amount to the baseLowHealthThreshhold updateFrequency = .2, -- the number of seconds before rechecking range/mana of a spell threatThreshold = 70, bloodlustMode = true, } }; -- random variables local lowHealthThreshold; local dotThreshold; local lastEclipse = 0; -- the last time eclipse fired -- KP1 local lastEclipseSOLAR = 0; -- the last time eclipse fired local lastEclipseLUNAR = 0; -- the last time eclipse fired local eclipseActive = false; local SOLAR_TYPE = true; local LUNAR_TYPE = false; local eclipseType = SOLAR_TYPE; local playerId; local timeLeft = 0; local sfDamage; local wrathDamage; local function shPrint(s) DEFAULT_CHAT_FRAME:AddMessage("SquawkHard: ".. tostring(s)); end local function printTable(table) for k,v in pairs(table) do shPrint(k .. '=' .. tostring(v)) end end function SquawkHard:RecalcHealthThresholds() groupCount = GetNumRaidMembers(); if groupCount == 0 then groupCount = GetNumPartyMembers(); else groupCount = groupCount - 1; -- don't include yourself end lowHealthThreshold = self.db.char.baseLowHealthThreshold + (self.db.char.lowHealthPartyModifier * groupCount); dotThreshold = self.db.char.baseDotThreshold + (self.db.char.dotThresholdPartyModifier * groupCount); --shPrint('lowHealth=' .. lowHealthThreshold .. ' dotThreshold=' .. dotThreshold); end -- estimate spell damage for wrath and starfire, this will be lower than actually damage because of buffs, etc local function RecalcSpellDamage() local playerLevel = UnitLevel('player'); -- do some simple spell damage calcs local baseSFDamage; if (playerLevel >= 78) then baseSFDamage = 1120; elseif playerLevel >= 72 then baseSFDamage = 930; elseif playerLevel >= 67 then baseSFDamage = 891; elseif playerLevel >= 60 then baseSFDamage = 755; elseif playerLevel >= 58 then baseSFDamage = 670; elseif playerLevel >= 50 then baseSFDamage = 530; elseif playerLevel >= 42 then baseSFDamage = 406; elseif playerLevel >= 34 then baseSFDamage = 300; elseif playerLevel >= 26 then baseSFDamage = 210; else baseSFDamage = 135; end local baseWrathDamage; if playerLevel >= 79 then baseWrathDamage = 588; elseif playerLevel >= 74 then baseWrathDamage = 536; elseif playerLevel >= 69 then baseWrathDamage = 405; elseif playerLevel >= 61 then baseWrathDamage = 295; elseif playerLevel >= 54 then baseWrathDamage = 250; elseif playerLevel >= 46 then baseWrathDamage = 199; elseif playerLevel >= 38 then baseWrathDamage = 148; elseif playerLevel >= 30 then baseWrathDamage = 108; elseif playerLevel >= 22 then baseWrathDamage = 68; elseif playerLevel >= 14 then baseWrathDamage = 48; elseif playerLevel >= 6 then baseWrathDamage = 27; elseif playerLevel >= 1 then baseWrathDamage = 13; end -- these calcs assume you have some key talents in the balance tree local arcaneBonusDamage = GetSpellBonusDamage(7); sfDamage = ((baseSFDamage + (1.2 * arcaneBonusDamage)) * 1.1) * 1.13; local natureBonusDamage = GetSpellBonusDamage(4); wrathDamage = ((baseWrathDamage + (.674 * natureBonusDamage)) * 1.1) * 1.13; --shPrint('sfDamage=' .. sfDamage .. ' wrathDamage=' .. wrathDamage); end function SquawkHard:OnDisable() shPrint(' disabled'); end function SquawkHard:SaveLocation() point, relativeTo, relativePoint, xOfs, yOfs = shFrame:GetPoint(); self.db.char.x = xOfs; self.db.char.y = yOfs; self.db.char.relativePoint = relativePoint; end function SquawkHard:OnInitialize() local AceConfigReg = LibStub("AceConfigRegistry-3.0") local AceConfigDialog = LibStub("AceConfigDialog-3.0") self.db = LibStub("AceDB-3.0"):New("SquawkHardDB", defaults, "char") LibStub("AceConfig-3.0"):RegisterOptionsTable("SquawkHard", self:GetOptions(), {"SquawkHard", "sqh"} ) self.optionsFrame = AceConfigDialog:AddToBlizOptions("SquawkHard","SquawkHard") self.db:RegisterDefaults(defaults); --printTable(self.db.char); shFrame = CreateFrame("Frame","SquawkHardDisplayFrame",UIParent) shFrame:SetFrameStrata("BACKGROUND") shFrame:SetWidth(self.db.char.size) shFrame:SetHeight(self.db.char.size) shFrame:SetBackdrop({ bgFile = "Interface\\Tooltips\\UI-Tooltip-Background", tile = true, tileSize = 32, }) shFrame:SetBackdropColor(0, 0, 0, .4) shFrame:EnableMouse(true) shFrame:SetMovable(true) shFrame:SetClampedToScreen(true) shFrame:SetScript("OnMouseDown", function(self) if not SquawkHard.db.char.locked then self:StartMoving(); end end); shFrame:SetScript("OnMouseUp", function(self) self:StopMovingOrSizing(); SquawkHard:SaveLocation(); end); shFrame:SetScript("OnDragStop", function(self) self:StopMovingOrSizing(); SquawkHard:SaveLocation(); end) shFrame:ClearAllPoints(); shFrame:SetPoint(self.db.char.relativePoint, self.db.char.x, self.db.char.y); cooldownFrame = CreateFrame("Cooldown","$parent_cooldown", shFrame); cooldownFrame:SetAllPoints(shFrame); if not shFrame.text then shFrame.text = shFrame:CreateFontString(nil,"OVERLAY") end local testfont = media:Fetch("font", 'Arial'); shFrame.text:SetFont(testfont, 12, "THICKOUTLINE"); --shFrame.text:SetFontObject(GameFontHighlightSmallOutline); shFrame.text:ClearAllPoints() shFrame.text:SetTextColor(.8, 0, 0, 1) shFrame.text:SetPoint("BOTTOM", shFrame, "BOTTOM", 0, -12); shFrame.text:SetText("") if not shFrame.alertText then shFrame.alertText = shFrame:CreateFontString(nil,"OVERLAY") end shFrame.alertText:SetFont(testfont, 10, "THICKOUTLINE"); shFrame.alertText:ClearAllPoints() shFrame.alertText:SetTextColor(1, 1, 0, 1) shFrame.alertText:SetPoint("BOTTOM", shFrame, "TOP", 3, 2); shFrame.alertText:SetText("") spellTexture = shFrame:CreateTexture(nil,"BACKGROUND"); overlayTexture = shFrame:CreateTexture(nil,"OVERLAY"); spellTexture:SetAllPoints(shFrame); overlayTexture:SetAllPoints(shFrame); end -- this colors the overlay frame based on range, mana, or eclipse function SquawkHard:StatusCheck(spell) local possessTexture = GetPossessInfo(1); if possessTexture then overlayTexture:SetTexture(.5, .5, .5, .5); elseif IsSpellInRange(spell, 'target') == 0 then overlayTexture:SetTexture(1, 0, 0, .5); else name, rank, icon, cost = GetSpellInfo(spell); if cost and UnitPower('player') < cost then overlayTexture:SetTexture(0, 0, 1, .5); elseif eclipseActive then if eclipseType == SOLAR_TYPE then overlayTexture:SetTexture(1, .7, 0, .5); else overlayTexture:SetTexture(1, 0, 1, .5); end else overlayTexture:SetTexture(nil); end end if groupCount == 0 or self.db.char.threatThreshold > 100 then shFrame.text:SetText(""); else local _,_,threatpct,_,_ = UnitDetailedThreatSituation("player", "target"); if not threatpct or threatpct < self.db.char.threatThreshold then shFrame.text:SetText(""); else shFrame.text:SetText(string.format("%.0f", threatpct) .. '%'); end end end function SquawkHard:CheckSpellUsability() local usable, nomana = IsUsableSpell(insectSwarmName); insectSwarmTrained = usable or nomana; for i = 1, GetNumGlyphSockets() do local enabled, _, glyphSpellID = GetGlyphSocketInfo(i); if enabled and glyphSpellID == 54845 then hasSFGlyph = true; --shPrint('detected starfire glyph'); end end end function SquawkHard:OnEnable() local playerClass, englishClass = UnitClass("player"); if englishClass ~= 'DRUID' then shPrint('You are not a druid. Please disable SquawkHard as an Addon for this character'); else shPrint('Enabled'); playerId = UnitGUID("player"); local faction = UnitFactionGroup('player'); if faction == "Horde" then bloodlustName = GetSpellInfo(2825); else bloodlustName = GetSpellInfo(32182); -- heroism for alliance end self:RecalcHealthThresholds(); RecalcSpellDamage(); self:CheckSpellUsability(); self:PLAYER_TARGET_CHANGED(); -- check target -- Register for Function Events self:UnregisterAllEvents(); self:RegisterEvent("PLAYER_TARGET_CHANGED"); self:RegisterEvent("UPDATE_SHAPESHIFT_FORM"); self:RegisterEvent("PARTY_MEMBERS_CHANGED"); self:RegisterEvent("PLAYER_REGEN_ENABLED"); self:RegisterEvent("PLAYER_REGEN_DISABLED"); self:RegisterEvent("CHARACTER_POINTS_CHANGED"); end end function SquawkHard:CHARACTER_POINTS_CHANGED(...) self:CheckSpellUsability(); end function SquawkHard:GetNextSpell() local targetCurrentHealth = UnitHealth('target'); if targetCurrentHealth == 1 then local unitName = UnitName('target'); if unitName and unitName:find('Training Dummy') then targetCurrentHealth = 999999; -- we're hitting a training dummy end end -- calculate the next time we can cast local spellName, _, _, _, _, spellEndTime = UnitCastingInfo('player'); if spellName then lastSpell = spellName; -- do a predictive adjustment on the health of the mob after current spell if spellName == starfireName then targetCurrentHealth = targetCurrentHealth - sfDamage; elseif spellName == wrathName then targetCurrentHealth = targetCurrentHealth - wrathDamage; end end local curTime = GetTime(); local nextCastTime = curTime; if spellEndTime then nextCastTime = spellEndTime / 1000; end -- also check for global cooldown local inCooldown = false; local startTime, duration = GetSpellCooldown(cooldownSpellName); if duration == nil then -- not high enough level to have cat form, so just wrath. lol startTime, duration = GetSpellCooldown(wrathName); if duration > 0 then cooldownFrame:SetCooldown(startTime, duration); end return wrathName; end if duration > 0 then local gcdEndTime = startTime + duration; if gcdEndTime > nextCastTime then nextCastTime = gcdEndTime; end cooldownFrame:SetCooldown(startTime, duration); cooldownFrame:SetAlpha(1); else cooldownFrame:SetAlpha(0); end local wrathStart, wrathDur = GetSpellCooldown(wrathName); local canCastWrathNext = wrathStart + wrathDur; local sfStart, sfDur = GetSpellCooldown(starfireName); local canCastSFNext = sfStart + sfDur; local natureLockout = (nextCastTime < canCastWrathNext); local arcaneLockout = (nextCastTime < canCastSFNext); local alertStr = ''; if natureLockout or arcaneLockout then local lockStr; if natureLockout and arcaneLockout then lockStr = 'SpellLock'; elseif natureLockout then lockStr = 'NatureLock'; else lockStr = 'ArcaneLock'; end alertStr = '[' .. lockStr .. ']'; end local _, _, _, _, _, _, wrathCastTime = GetSpellInfo(wrathName); wrathCastTime = wrathCastTime / 1000; local _, _, _, _, _, _, sfCastTime = GetSpellInfo(starfireName); sfCastTime = sfCastTime / 1000; -- check on our eclipse status local timeSinceEclipse = nextCastTime - lastEclipse; -- KP1 local timeSinceEclipseSOLAR = nextCastTime - lastEclipseSOLAR; local timeSinceEclipseLUNAR = nextCastTime - lastEclipseLUNAR; local eclipseDurationLeft = 0; --DEFAULT_CHAT_FRAME:AddMessage("KP SOLAR: ".. tostring(timeSinceEclipseSOLAR)); --DEFAULT_CHAT_FRAME:AddMessage("KP LUNAR: ".. tostring(timeSinceEclipseLUNAR)); if eclipseActive then eclipseDurationLeft = eclipseDuration - timeSinceEclipse; if eclipseType == SOLAR_TYPE then eclipseActive = eclipseDurationLeft > wrathCastTime; else eclipseActive = eclipseDurationLeft > sfCastTime; end end -- check target buffs local buffName = nil; local nastySpellName; -- for tracking nasty spell reflect debuffs for i=1,40 do local name = UnitBuff("target", i); if not name then break; -- end of buffs end if name:lower():find('reflect') then nastySpellName = name; break; end end alertStr = ''; if nastySpellName then alertStr = alertStr .. '[' .. nastySpellName .. ']'; end shFrame.alertText:SetText(alertStr); if targetCurrentHealth < lowHealthThreshold then if targetCurrentHealth == 0 then return nil; end return wrathName; end local shortDot = false; -- flag to cast wrath because dot is expiring soon -- check for heroism/bloodlust local bloodlustActive = false; if self.db.char.bloodlustMode then for i=1,40 do local name = UnitBuff("player", i); if not name then break; -- end of buffs end if name == bloodlustName then bloodlustActive = true; break; end end end -- apply dots if necessary if targetCurrentHealth > dotThreshold then -- determine dot durations local isTimeLeft = -1; local mfTimeLeft = -1; local ffTimeLeft = -1; -- check target debuffs for i=1,40 do local name, _, _, _, _, _, expirationTime, isMine = UnitDebuff("target", i); if not name then break; -- end of debuffs end if isMine then if name == insectSwarmName then isTimeLeft = expirationTime - nextCastTime; elseif name == moonfireName then mfTimeLeft = expirationTime - nextCastTime; end end if name == faerieFireName then ffTimeLeft = expirationTime - nextCastTime; end end if self.db.char.useFaerieFire and ffTimeLeft <= 0.0 and (not eclipseActive) and (not natureLockout) then return faerieFireName; end if bloodlustActive and (not arcaneLockout) then if mfTimeLeft <= 0.0 then return moonfireName; end return starfireName; end -- see if we should recast a dot if self.db.char.useInsectSwarm and insectSwarmTrained and isTimeLeft <= 0.0 and (not natureLockout) then if eclipseActive then if eclipseType == LUNAR_TYPE then if eclipseDurationLeft > self.db.char.lunarEclipseInsectSwarmThreshold then return insectSwarmName; end elseif eclipseDurationLeft > self.db.char.solarEclipseInsectSwarmThreshold then return insectSwarmName; end else return insectSwarmName; end end if mfTimeLeft <= 0.0 and (not arcaneLockout) then if eclipseActive then if eclipseType == LUNAR_TYPE then if eclipseDurationLeft > self.db.char.lunarEclipseMoonfireThreshold then return moonfireName; end elseif eclipseDurationLeft > self.db.char.solarEclipseMoonfireThreshold then return moonfireName; end else return moonfireName; end end shortDot = (isTimeLeft > 0 and isTimeLeft < wrathCastTime) or (mfTimeLeft > 0 and mfTimeLeft < wrathCastTime); -- if we can still extend moonfire disable the shortDot optimization if shortDot and hasSFGlyph and mfTimeLeft > sfCastTime then local mfExtends = moonfireExtends; if spellName and spellName == starfireName then mfExtends = moonfireExtends + 1; end if moonfireExtends < 3 then shortDot = false; -- disable short dot optimization if we still need to extend moonfire end end end if natureLockout or bloodlustActive then return starfireName; end if arcaneLockout then return wrathName; end local eclipseProcType; if targetCurrentHealth >= (dotThreshold * 2) then eclipseProcType = self.db.char.highHealthEclipseProcType; else eclipseProcType = self.db.char.lowHealthEclipseProcType; end -- main spell spam if eclipseActive then if eclipseType == SOLAR_TYPE then return wrathName; else return starfireName; end elseif (timeSinceEclipseSOLAR < eclipseCooldown) and (timeSinceEclipseLUNAR < eclipseCooldown) then -- KP1 -- TWO eclipse is on cooldown if shortDot then return wrathName; elseif eclipseProcSpell == wrathName and (timeSinceEclipse + sfCastTime + .2 >= eclipseCooldown) then -- next starfire will finish after eclipseCooldown so cast wrath return wrathName; else return starfireName; end elseif (timeSinceEclipseSOLAR > eclipseCooldown) and (timeSinceEclipseLUNAR < eclipseCooldown) then -- KP1 -- LUNAR eclipse on cooldown, try to proc SOLAR (Starfire) return starfireName; elseif (timeSinceEclipseSOLAR < eclipseCooldown) and (timeSinceEclipseLUNAR > eclipseCooldown) then -- KP1 -- SOLAR eclipse on cooldown, try to proc LUNAR (Wrath) return wrathName; else -- KP1 both eclipse (SOLAR and LUNAR) NOT on CD, original code if eclipseProcType == 1 or shortDot then return wrathName; elseif eclipseProcType == 2 then return starfireName else -- twist if lastSpell == starfireName then return wrathName; elseif lastSpell == wrathName then return starfireName; elseif eclipseProcType == 3 then return wrathName; else return starfireName; end end end end function SquawkHard:PLAYER_REGEN_ENABLED(...) self:UnregisterEvent("COMBAT_LOG_EVENT_UNFILTERED"); end function SquawkHard:PLAYER_REGEN_DISABLED(...) RecalcSpellDamage(); SquawkHard:PLAYER_TARGET_CHANGED(); -- recompute if target is friend when combat starts self:RegisterEvent("COMBAT_LOG_EVENT_UNFILTERED"); end function SquawkHard:RecalcSpell() local nextSpell = SquawkHard:GetNextSpell(); if nextSpell then spellTexture:SetTexture(GetSpellTexture(nextSpell)); self:StatusCheck(nextSpell); end timeLeft = SquawkHard.db.char.updateFrequency; end function SquawkHard:UPDATE_SHAPESHIFT_FORM(...) self:PLAYER_TARGET_CHANGED(); end function SquawkHard:PLAYER_TARGET_CHANGED(...) local shapeshiftNum = GetShapeshiftForm(); local canCast = (shapeshiftNum == 0) or (shapeshiftNum == 5); if (not canCast) or UnitName("target") == nil or (not UnitCanAttack("player","target")) or UnitHealth("target") == 0 then if self.db.char.locked then shFrame:Hide(); else shFrame:Show(); end if activeMode then activeMode = false; lastSpell = nil; shFrame:SetScript('OnUpdate', nil); end else shFrame:Show(); if not activeMode then activeMode = true; timeLeft = 0; shFrame:SetScript('OnUpdate', function(self, timeSinceLast) timeLeft = timeLeft - timeSinceLast; if timeLeft <= 0 then SquawkHard:RecalcSpell(); end end) end end end function SquawkHard:COMBAT_LOG_EVENT_UNFILTERED(_, timestamp, event, sourceGUID, sourceName, sourceFlags, destGUID, destName, destFlags, ...) if sourceGUID == playerId then if event == "SPELL_AURA_APPLIED" then local spellID = select(1, ...); if spellID == solarEclipseId then lastEclipse = GetTime(); -- KP1 lastEclipseSOLAR = GetTime(); eclipseActive = true; eclipseType = SOLAR_TYPE; timeLeft = SquawkHard.db.char.updateFrequency; -- this is to try and avoid a double recalc self:RecalcSpell(); elseif spellID == lunarEclipseId then lastEclipse = GetTime(); -- KP1 lastEclipseLUNAR = GetTime(); eclipseActive = true; eclipseType = LUNAR_TYPE; timeLeft = SquawkHard.db.char.updateFrequency; -- this is to try and avoid a double recalc self:RecalcSpell(); end elseif event == "SPELL_CAST_SUCCESS" or event == "SPELL_DAMAGE" then local spellName = select(2, ...); lastSpell = spellName; if lastSpell == moonfireName then moonfireExtends = 0; elseif lastSpell == starfireName and moonfireExtends < 3 then moonfireExtends = moonfireExtends + 1; end end end end function SquawkHard:PARTY_MEMBERS_CHANGED(...) self:RecalcHealthThresholds(); end function SquawkHard:GetOptions() local options = { name = "SquawkHard", handler = SquawkHard, type = 'group', childGroups ='tree', args = { locked = { type = "toggle", name = "Locked", get = "GetProperty", set = "SetProperty", order = 0, }, size = { type = "range", name = "Size", desc = "Size of the Frame", min = 1, max = 200, step = 1, get = "GetProperty", set = "SetProperty", order = 1, }, lowHealthEclipseProcType = { type = 'select', name = "Low Health Eclipse Proc Type", values = { wrathName, starfireName, 'Twist (Wrath first)', 'Twist (Starfire first)' }, desc = "Spell Used to Proc Eclipse for enemies with less than 2*dotThreshold health. This is the spell you cast to try and get an eclipse. Not the one you cast while eclipse is active", get = "GetProperty", set = "SetProperty", order = 2, }, highHealthEclipseProcType = { type = 'select', name = "High Health Eclipse Proc Type", values = { wrathName, starfireName, 'Twist (Wrath first)', 'Twist (Starfire first)' }, desc = "Spell Used to Proc Eclipse on enemies with atleast 2*dotThreshold health. This is the spell you cast to try and get an eclipse. Not the one you cast while eclipse is active", get = "GetProperty", set = "SetProperty", order = 3, }, useFaerieFire = { type = 'toggle', name = "Faerie Fire", desc = "Include Faerie Fire in your rotation", get = "GetProperty", set = "SetProperty", order = 4, }, useInsectSwarm = { type = 'toggle', name = "Insect Swarm", desc = "Include Insect Swarm in your rotation. If this is disabled, the IS Threshold properties are unused", get = "GetProperty", set = "SetProperty", order = 5, }, bloodlustMode = { type = 'toggle', name = "Bloodlust Mode", desc = "While bloodlust is active only cast Moonfire and Starfire (and Faerie Fire if checked)", get = "GetProperty", set = "SetProperty", order = 6, width = "full", }, lunarEclipseMoonfireThreshold = { type = 'range', name = 'Lunar Moonfire Threshold', desc = "Atleast this many seconds left on lunar eclipse to consider casting moonfire. Set to 16 seconds if you don't want to cast it at all. (Lunar Eclipse is procced by Wrath)", min = 0, max = 16, step = .5, get = "GetProperty", set = "SetProperty", order = 7, }, lunarEclipseInsectSwarmThreshold = { type = 'range', name = 'Lunar IS Threshold', desc = "Atleast this many seconds left on lunar eclipse to consider casting insect swarm. Set to 16 seconds if you don't want to cast it at all. (Lunar Eclipse is procced by Wrath)", min = 0, max = 16, step = .5, get = "GetProperty", set = "SetProperty", order = 8, }, solarEclipseMoonfireThreshold = { type = 'range', name = 'Solar Moonfire Threshold', desc = "Atleast this many seconds left on solar eclipse to consider casting moonfire. Set to 16 seconds if you don't want to cast it at all. (Solar Eclipse is procced by Starfire)", min = 0, max = 16, step = .5, get = "GetProperty", set = "SetProperty", order = 9, }, solarEclipseInsectSwarmThreshold = { type = 'range', name = 'Solar IS Threshold', desc = "Atleast this many seconds left on solar eclipse to consider casting insect swarm. Set to 16 seconds if you don't want to cast it at all. (Solar Eclipse is procced by Starfire)", min = 0, max = 16, step = .5, get = "GetProperty", set = "SetProperty", order = 10, }, baseDotThreshold = { type = 'range', name = "Dot Threshold", desc = "Do not cast dots if the targets hp is less than this", min = 0, max = 100000, step = 1000, get = "GetProperty", set = "SetProperty", order = 11, }, dotThresholdPartyModifier = { type = 'range', name = "Party Modifier", desc = "Add this value to dot threshold for each additional party or raid member", min = 0, max = 30000, step = 500, get = "GetProperty", set = "SetProperty", order = 12, }, baseLowHealthThreshold = { type = 'range', name = "Low Health Threshold", desc = "If the targets hp gets below this level just cast Wrath.", min = 0, max = 15000, step = 250, get = "GetProperty", set = "SetProperty", order = 13, }, lowHealthPartyModifier = { type = 'range', name = "Party Modifier", desc = "Adds this value to the Low Health Threshold for each additional party or raid member", min = 0, max = 10000, step = 500, get = "GetProperty", set = "SetProperty", order = 14, }, updateFrequency = { type = 'range', name = "Update Frequency", desc = "The delay in seconds to wait before rechecking the next spell.", min = .05, max = .5, step = .05, get = "GetProperty", set = "SetProperty", order = 15, }, threatThreshold = { type = 'range', name = "Threat", desc = "Show threat if your threat percentage is atleast this amount. 100% means you have aggro! Set to 101 if you don't want to see the threat indicator at all.", min = 0, max = 101, step = 1, get = "GetProperty", set = "SetProperty", order = 16, } }, } return options end function SquawkHard:GetProperty(info) local propName = info[#info]; local value = self.db.char[propName]; return value; end function SquawkHard:SetProperty(info, newValue) local propName = info[#info]; self.db.char[propName] = newValue; if shFrame and propName == 'size' then shFrame:SetWidth(self.db.char.size); shFrame:SetHeight(self.db.char.size); end self:PLAYER_TARGET_CHANGED(); -- recheck if frame should be visible or not self:RecalcHealthThresholds(); end